package hmvc4gwt.client.component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import hmvc4gwt.client.controller.IController;
import hmvc4gwt.client.event.IHmvcEvent;
import hmvc4gwt.client.event.IHmvcEventListener;
import hmvc4gwt.client.model.IModel;
import hmvc4gwt.client.view.IView;

/**
 * implements the basic functionality needed in every Component
 * 
 * @author jens.meiss
 *
 */

public abstract class AbstractComponent implements IComponent {
	
	/**
	 * contains the Controller of the Component
	 */
	private IController controller;
	
	/**
	 * contains all Listeners of the Component,
	 * <String>,<List>
	 * TODO HashMap initialSize setzen
	 */
	private Map mapEventListener = new HashMap(5);
	
	/**
	 * contains the Model of the Component
	 */
	private IModel model;
	
	/**
	 * contains the parent-Component
	 */
	private IComponent parentComponent;
	
	/**
	 * was IComponent.terminate() called
	 */
	private boolean terminated = false;
	
	/**
	 * contains the View of the Component
	 */
	private IView view;
	
	/**
	 * contains all Terminated Child Components
	 */
	private List listTerminatedChilds = new ArrayList(4);
	
	public AbstractComponent() {
		
	}
	
	protected void createComponent (IComponent parentComponent, IController controller) {
		
		setParentComponent(parentComponent);
		setController(controller);
		
		init();
	}

	protected void createComponent (IComponent parentComponent, IController controller, IModel model) {
		
		setParentComponent(parentComponent);
		setController(controller);
		setModel(model);
		
		init();
	}
	
	protected void createComponent (IComponent parentComponent, IController controller,
			IModel model, IView view) {
		
		setParentComponent(parentComponent);
		setController(controller);
		setModel(model);
		setView(view);
		
		init();
	}

	protected void createComponent (IComponent parentComponent, IController controller, IView view) {
		
		setParentComponent(parentComponent);
		setController(controller);
		setView(view);
		
		init();
	}

	protected void createComponent(IController controller) {
		setController(controller);
		
		init();
	}
	
	protected void createComponent (IController controller, IModel model) {
		setController(controller);
		setModel(model);
		
		init();
	}
	
	protected void createComponent (IController controller,
			IModel model, IView view) {
		
		setController(controller);
		setModel(model);
		setView(view);
		
		init();
	}

	protected void createComponent (IController controller, IView view) {
		setController(controller);
		setView(view);
		
		init();
	}

	/**
	 * adds an Child-Component to the vectorChildComponents
	 * @param component
	 * @return
	 */
	protected abstract IComponent addChildComponent(IComponent component);

	/**
	 * fires the Event to all local Listeners
	 * @param event
	 */
	protected abstract void callLocalListeners(IHmvcEvent event);

	private void connectTriadParts() {
		/*
		 * connect Controller
		 */
		controller.setComponent(this);

		/*
		 * connect the Model
		 */
		if (model != null) {
			model.setController(controller);
		}

		/*
		 * connect the View
		 */
		if (view != null) {
			view.setController(controller);
		}
	}

	public IController getController() {
		return controller;
	}

	protected Map getMapEventListener() {
		return mapEventListener;
	}
	
	public IModel getModel() {
		return model;
	}

	public IComponent getParentComponent() {
		return parentComponent;
	}

	public IView getView() {
		return view;
	}
	
	public void init() {
		connectTriadParts();
		initTriadParts();
	}
	
	private void initTriadParts() {
		
		/*
		 * call the pre() Method of every part
		 */
		if (model != null) {
			model.pre();
		}

		if (view != null) {
			view.pre();
		}

		if (controller != null) {
			controller.pre();
		}
		
		/*
		 * call the doInit()-Methode
		 */
		if (model != null) {
			model.init();
		}

		if (view != null) {
			view.init();
		}

		if (controller != null) {
			controller.init();
		}
		
		/*
		 * call the post()-Methode
		 */
		if (model != null) {
			model.post();
		}

		if (view != null) {
			view.post();
		}

		if (controller != null) {
			controller.post();
		}
	}
	
	public boolean isTerminated() {
		return terminated;
	}

	public void register(Class classOfEvent, IHmvcEventListener listener) {
		/*
		 * exists an List to store Listeners for the Event
		 */
		List listListener = null;
		if (mapEventListener.containsKey(classOfEvent)) {
			
			listListener = (List) mapEventListener.get(classOfEvent);
			
		} else {
			
			/*
			 * create new Vector to hold Listener for this Event 
			 * TODO initialCapacity setzen
			 */
			listListener = new ArrayList();
			mapEventListener.put(classOfEvent, listListener);
		}
		
		listListener.add(listener);
	}

	public void removeListener(Class classOfEvent, IHmvcEventListener listener) {
		
		/*
		 * exists Listeners for this Event
		 */
		if (mapEventListener.containsKey(classOfEvent)) {
			
			/*
			 * remove Listener of this Event
			 */
			List listListeners = (List) mapEventListener.get(classOfEvent);
			listListeners.remove(listener);
			
			/*
			 * clean-up empty Lists
			 */
			if (listListeners.size() == 0) {
				mapEventListener.remove(classOfEvent);
			}
		}
	}
	
	public void setController(IController controller) {
		this.controller = controller;
	}
	
	public void setModel(IModel model) {
		this.model = model;
	}

	public void setParentComponent(IComponent parentComponent) {
		this.parentComponent = parentComponent;
	}

	public void setTerminated(boolean b) {
		this.terminated = b;
	}
	
	public void setView(IView view) {
		this.view = view;
	}
	
	/**
	 * terminates the component and all its childs
	 */
	public void terminate() {
		
		terminateChildComponents();
		removeTerminatedChildComponents();

		setTerminated(true);
		
		if (view != null) {
			view.setTerminated(true);
			view.terminate();
		}
		
		if (model != null) {
			model.setTerminated(true);
			model.terminate();
		}
		
		controller.setTerminated(true);
		controller.terminate();

		if (parentComponent != null) {
			parentComponent.addTerminatedChildComponent(this);
		}
	}

	/**
	 * calls terminate() on all child-components
	 */
	protected abstract void terminateChildComponents();
	
	public void addTerminatedChildComponent(IComponent component) {
		listTerminatedChilds.add(component);
	}

	public List getListTerminatedChildComponents() {
		return listTerminatedChilds;
	}
}
